package poidemo.docx4j;

import java.util.concurrent.atomic.AtomicLong;
import javax.xml.bind.Unmarshaller;
import org.docx4j.XmlUtils;
import org.docx4j.convert.out.html.HTMLConversionContext;
import org.docx4j.convert.out.html.HtmlCssHelper;
import org.docx4j.jaxb.Context;
import org.docx4j.model.properties.paragraph.Indent;
import org.docx4j.model.styles.StyleTree;
import org.docx4j.model.styles.StyleTree.AugmentedStyle;
import org.docx4j.model.styles.Tree;
import org.docx4j.wml.PPr;
import org.docx4j.wml.PPrBase.Ind;
import org.docx4j.wml.RPr;
import org.docx4j.wml.Style;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.w3c.dom.traversal.NodeIterator;

public class HexinXsltHTMLFunctions {

	private static final Logger logger =
			LoggerFactory.getLogger(HexinXsltHTMLFunctions.class);

	public static DocumentFragment createBlockForPPr(
			HTMLConversionContext context,
			NodeIterator pPrNodeIt,
			String pStyleVal, NodeIterator childResults, NodeIterator hexinId) {

		return createBlock(
				context,
				pPrNodeIt,
				pStyleVal, childResults,
				"p", hexinId);

	}


	private static DocumentFragment createBlock(
			HTMLConversionContext context,
			NodeIterator pPrNodeIt,
			String pStyleVal, NodeIterator childResults,
			String htmlElementName) {

		StyleTree styleTree = context.getWmlPackage().getMainDocumentPart()
		                             .getStyleTree();

		// Note that this is invoked for every paragraph with a pPr node.

		// incoming objects are org.apache.xml.dtm.ref.DTMNodeIterator
		// which implements org.w3c.dom.traversal.NodeIterator

		Style defaultParagraphStyle =
				(context.getWmlPackage().getMainDocumentPart()
				        .getStyleDefinitionsPart(false) != null ?
						context.getWmlPackage().getMainDocumentPart()
						       .getStyleDefinitionsPart(false).getDefaultParagraphStyle() :
						null);

		String defaultParagraphStyleId;
		if (defaultParagraphStyle == null) // possible, for non MS source docx
		{
			defaultParagraphStyleId = "Normal";
		} else {
			defaultParagraphStyleId = defaultParagraphStyle.getStyleId();
		}

		if (pStyleVal == null || pStyleVal.equals("")) {
//			pStyleVal = "Normal";
			pStyleVal = defaultParagraphStyleId;
		}
		context.getLog().debug("style '" + pStyleVal);

//    	log.info("pPrNode:" + pPrNodeIt.getClass().getName() ); // org.apache.xml.dtm.ref.DTMNodeIterator
//    	log.info("childResults:" + childResults.getClass().getName() );

		try {

			// Get the pPr node as a JAXB object,
			// so we can read it using our standard
			// methods.  Its a bit sad that we
			// can't just adorn our DOM tree with the
			// original JAXB objects?
			PPr pPr = null;
			if (pPrNodeIt != null) { //It is never null
				Node n = pPrNodeIt.nextNode();
				if (n != null) {
					Unmarshaller u = Context.jc.createUnmarshaller();
					u.setEventHandler(new org.docx4j.jaxb.JaxbValidationEventHandler());
					Object jaxb = u.unmarshal(n);
					try {
						pPr = (PPr) jaxb;
					} catch (ClassCastException e) {
						context.getLog().error(
								"Couldn't cast " + jaxb.getClass().getName() + " to PPr!");
					}
				}
			}

			// Create a DOM document to take the results
			Document document = XmlUtils.getNewDocumentBuilder().newDocument();
			//log.info("Document: " + document.getClass().getName() );
			Element xhtmlBlock = document.createElement(htmlElementName);
			document.appendChild(xhtmlBlock);

			// 设置hexinId属性
			hexinId(xhtmlBlock);

			if (context.getLog().isDebugEnabled() && pPr != null) {
				context.getLog().debug(XmlUtils.marshaltoString(pPr, true, true));
			}

			// Set @class
			context.getLog().debug(pStyleVal);
			Tree<AugmentedStyle> pTree = styleTree.getParagraphStylesTree();
			org.docx4j.model.styles.Node<AugmentedStyle> asn = pTree.get(pStyleVal);
			String classVal = StyleTree.getHtmlClassAttributeValue(pTree, asn);
			xhtmlBlock.setAttribute("class", classVal);

			// Does our pPr contain anything else?
			boolean ignoreBorders = (htmlElementName.equals("p"));
			if (pPr != null) {

				// Is there numbering indentation to honour?
				if (pPr.getNumPr() != null
						&& pPr.getNumPr().getNumId() != null
						&& pPr.getNumPr().getNumId().getVal().longValue()
						!= 0 //zero means no numbering
				) {
					Ind numInd = org.docx4j.model.listnumbering.Emulator.getInd(
							context.getWmlPackage(), pStyleVal,
							pPr.getNumPr().getNumId().getVal().toString(),
							pPr.getNumPr().getIlvl().getVal().toString());
					if (numInd != null) {
						Indent indent = new Indent(pPr.getInd(), numInd);
						pPr.setInd((Ind) indent.getObject());
					}
				}

				StringBuilder inlineStyle = new StringBuilder();
				HtmlCssHelper
						.createCss(context.getWmlPackage(), pPr, inlineStyle, ignoreBorders,
								xhtmlBlock.getNodeName().equals("li"));
				if (!inlineStyle.toString().equals("")) {
					xhtmlBlock.setAttribute("style", inlineStyle.toString());
				}
			}

			// Our element (eg <p>) wraps whatever result tree fragment
			// our style sheet produced when it applied-templates
			// to the child nodes
			// init
			Node n = childResults.nextNode();

			if (xhtmlBlock.getNodeName().equals("p")
					&& context.getBookmarkStart() != null) {

				xhtmlBlock.setAttribute("id", context.getBookmarkStart().getName());
				context.setBookmarkStart(null);
			}

			if (xhtmlBlock.getNodeName().equals("p")
					&& n.hasChildNodes()
					&& n.getChildNodes().item(0).getLocalName().equals("span")) {
				// old XSLT won't produce a span for w:r unless there is w:rPr

				mergeSpans(n.getChildNodes(), document, xhtmlBlock);

			} else {

				do {

					//				System.out.println("\n\n" + XmlUtils.w3CDomNodeToString(n) + "\n\n");

					// getNumberXmlNode creates a span node, which is empty
					// if there is no numbering.
					// Let's get rid of any such <span/>.

					// What we actually get is a document node
					if (n.getNodeType() == Node.DOCUMENT_NODE) {
						context.getLog().debug("handling DOCUMENT_NODE");

						// Do just enough of the handling here
						NodeList nodes = n.getChildNodes();
						if (nodes != null) {
							for (int i = 0; i < nodes.getLength(); i++) {

								if (((Node) nodes.item(i)).getLocalName().equals("span")
										&& !((Node) nodes.item(i)).hasChildNodes()) {
									// ignore
									context.getLog().debug(".. ignoring <span/> ");
								} else {
									XmlUtils.treeCopy((Node) nodes.item(i), xhtmlBlock);
								}
							}
						}


					} else {

						//					log.info("Node we are importing: " + n.getClass().getName() );
						//					foBlockElement.appendChild(
						//							document.importNode(n, true) );
						/*
						 * Node we'd like to import is of type org.apache.xml.dtm.ref.DTMNodeProxy
						 * which causes
						 * org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation.
						 *
						 * See http://osdir.com/ml/text.xml.xerces-j.devel/2004-04/msg00066.html
						 *
						 * So instead of importNode, use
						 */
						XmlUtils.treeCopy(n, xhtmlBlock);
					}
					// next
					n = childResults.nextNode();

				} while (n != null);
			}

			if (xhtmlBlock.getNodeName().equals("p")
					&& !xhtmlBlock.hasChildNodes()) {
				// browsers don't display an empty p, so add a space to it

				Text t = document.createTextNode("\u00A0"); //= &nbsp; = &#160;
				// see notes in docx2xhtmlNG2.xslt as to why it is done this way!
				xhtmlBlock.appendChild(t);
			}

//			System.out.println(XmlUtils.w3CDomNodeToString(document));

			DocumentFragment docfrag = document.createDocumentFragment();
			docfrag.appendChild(document.getDocumentElement());

			return docfrag;

		} catch (Exception e) {
			context.getLog().error(e.getMessage(), e);
		}

		return null;
	}

	private static DocumentFragment createBlock(
			HTMLConversionContext context,
			NodeIterator pPrNodeIt,
			String pStyleVal, NodeIterator childResults,
			String htmlElementName, NodeIterator currentNode) {

		Node curNode = currentNode.nextNode();
		if (curNode!=null) {
			logger.info("currentNodeStr:"+curNode.getTextContent());
			logger.info("currentNode: "+curNode.getNodeName());
		} else {
			logger.info("currentNode: " + null);
		}


		StyleTree styleTree = context.getWmlPackage().getMainDocumentPart()
		                             .getStyleTree();

		// Note that this is invoked for every paragraph with a pPr node.

		// incoming objects are org.apache.xml.dtm.ref.DTMNodeIterator
		// which implements org.w3c.dom.traversal.NodeIterator

		Style defaultParagraphStyle =
				(context.getWmlPackage().getMainDocumentPart()
				        .getStyleDefinitionsPart(false) != null ?
						context.getWmlPackage().getMainDocumentPart()
						       .getStyleDefinitionsPart(false).getDefaultParagraphStyle() :
						null);

		String defaultParagraphStyleId;
		if (defaultParagraphStyle == null) // possible, for non MS source docx
		{
			defaultParagraphStyleId = "Normal";
		} else {
			defaultParagraphStyleId = defaultParagraphStyle.getStyleId();
		}

		if (pStyleVal == null || pStyleVal.equals("")) {
//			pStyleVal = "Normal";
			pStyleVal = defaultParagraphStyleId;
		}
		context.getLog().debug("style '" + pStyleVal);

//    	log.info("pPrNode:" + pPrNodeIt.getClass().getName() ); // org.apache.xml.dtm.ref.DTMNodeIterator
//    	log.info("childResults:" + childResults.getClass().getName() );

		try {

			// Get the pPr node as a JAXB object,
			// so we can read it using our standard
			// methods.  Its a bit sad that we
			// can't just adorn our DOM tree with the
			// original JAXB objects?
			PPr pPr = null;
			if (pPrNodeIt != null) { //It is never null
				Node n = pPrNodeIt.nextNode();
				if (n != null) {
					Unmarshaller u = Context.jc.createUnmarshaller();
					u.setEventHandler(new org.docx4j.jaxb.JaxbValidationEventHandler());
					Object jaxb = u.unmarshal(n);
					try {
						pPr = (PPr) jaxb;
					} catch (ClassCastException e) {
						context.getLog().error(
								"Couldn't cast " + jaxb.getClass().getName() + " to PPr!");
					}
				}
			}

			// Create a DOM document to take the results
			Document document = XmlUtils.getNewDocumentBuilder().newDocument();
			//log.info("Document: " + document.getClass().getName() );
			Element xhtmlBlock = document.createElement(htmlElementName);
			document.appendChild(xhtmlBlock);

			// 设置hexinId属性
			hexinId(xhtmlBlock);

			if (context.getLog().isDebugEnabled() && pPr != null) {
				context.getLog().debug(XmlUtils.marshaltoString(pPr, true, true));
			}

			// Set @class
			context.getLog().debug(pStyleVal);
			Tree<AugmentedStyle> pTree = styleTree.getParagraphStylesTree();
			org.docx4j.model.styles.Node<AugmentedStyle> asn = pTree.get(pStyleVal);
			String classVal = StyleTree.getHtmlClassAttributeValue(pTree, asn);
			xhtmlBlock.setAttribute("class", classVal);

			// Does our pPr contain anything else?
			boolean ignoreBorders = (htmlElementName.equals("p"));
			if (pPr != null) {

				// Is there numbering indentation to honour?
				if (pPr.getNumPr() != null
						&& pPr.getNumPr().getNumId() != null
						&& pPr.getNumPr().getNumId().getVal().longValue()
						!= 0 //zero means no numbering
				) {
					Ind numInd = org.docx4j.model.listnumbering.Emulator.getInd(
							context.getWmlPackage(), pStyleVal,
							pPr.getNumPr().getNumId().getVal().toString(),
							pPr.getNumPr().getIlvl().getVal().toString());
					if (numInd != null) {
						Indent indent = new Indent(pPr.getInd(), numInd);
						pPr.setInd((Ind) indent.getObject());
					}
				}

				StringBuilder inlineStyle = new StringBuilder();
				HtmlCssHelper
						.createCss(context.getWmlPackage(), pPr, inlineStyle, ignoreBorders,
								xhtmlBlock.getNodeName().equals("li"));
				if (!inlineStyle.toString().equals("")) {
					xhtmlBlock.setAttribute("style", inlineStyle.toString());
				}
			}

			// Our element (eg <p>) wraps whatever result tree fragment
			// our style sheet produced when it applied-templates
			// to the child nodes
			// init
			Node n = childResults.nextNode();

			if (xhtmlBlock.getNodeName().equals("p")
					&& context.getBookmarkStart() != null) {

				xhtmlBlock.setAttribute("id", context.getBookmarkStart().getName());
				context.setBookmarkStart(null);
			}

			if (xhtmlBlock.getNodeName().equals("p")
					&& n.hasChildNodes()
					&& n.getChildNodes().item(0).getLocalName().equals("span")) {
				// old XSLT won't produce a span for w:r unless there is w:rPr

				mergeSpans(n.getChildNodes(), document, xhtmlBlock);

			} else {

				do {

					//				System.out.println("\n\n" + XmlUtils.w3CDomNodeToString(n) + "\n\n");

					// getNumberXmlNode creates a span node, which is empty
					// if there is no numbering.
					// Let's get rid of any such <span/>.

					// What we actually get is a document node
					if (n.getNodeType() == Node.DOCUMENT_NODE) {
						context.getLog().debug("handling DOCUMENT_NODE");

						// Do just enough of the handling here
						NodeList nodes = n.getChildNodes();
						if (nodes != null) {
							for (int i = 0; i < nodes.getLength(); i++) {

								if (((Node) nodes.item(i)).getLocalName().equals("span")
										&& !((Node) nodes.item(i)).hasChildNodes()) {
									// ignore
									context.getLog().debug(".. ignoring <span/> ");
								} else {
									XmlUtils.treeCopy((Node) nodes.item(i), xhtmlBlock);
								}
							}
						}


					} else {

						//					log.info("Node we are importing: " + n.getClass().getName() );
						//					foBlockElement.appendChild(
						//							document.importNode(n, true) );
						/*
						 * Node we'd like to import is of type org.apache.xml.dtm.ref.DTMNodeProxy
						 * which causes
						 * org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation.
						 *
						 * See http://osdir.com/ml/text.xml.xerces-j.devel/2004-04/msg00066.html
						 *
						 * So instead of importNode, use
						 */
						XmlUtils.treeCopy(n, xhtmlBlock);
					}
					// next
					n = childResults.nextNode();

				} while (n != null);
			}

			if (xhtmlBlock.getNodeName().equals("p")
					&& !xhtmlBlock.hasChildNodes()) {
				// browsers don't display an empty p, so add a space to it

				Text t = document.createTextNode("\u00A0"); //= &nbsp; = &#160;
				// see notes in docx2xhtmlNG2.xslt as to why it is done this way!
				xhtmlBlock.appendChild(t);
			}

//			System.out.println(XmlUtils.w3CDomNodeToString(document));

			DocumentFragment docfrag = document.createDocumentFragment();
			docfrag.appendChild(document.getDocumentElement());

			return docfrag;

		} catch (Exception e) {
			context.getLog().error(e.getMessage(), e);
		}

		return null;
	}

	/**
	 * Merge adjacent spans if @class and @style are the same
	 */
	private static void mergeSpans(NodeList nodes, Document document,
			Element xhtmlBlock) {

		if (nodes == null || nodes.getLength() == 0) {
			return;
		}

		// init .. skip children until we find a span
		int startIndex = 0;
		Element currentEl;
		while (true) {
			currentEl = ((Element) nodes.item(startIndex));

			if (currentEl.getLocalName().equals("span")) {
				break;
			} else {
				XmlUtils.treeCopy(currentEl, xhtmlBlock);
			}

			startIndex++;
			if (startIndex == nodes.getLength()) {
				return;
			}
		}

		Element currentSpan = currentEl;
		String currentClass = currentSpan.getAttribute("class");
		String currentStyle = currentSpan.getAttribute("style");
		// should work for anything with @class, @style

		// Can't reuse existing span, since we'll get org.apache.xml.dtm.DTMDOMException
		Element newSpan = document.createElement("span");
		if (currentClass != null) {
			newSpan.setAttribute("class", currentClass);
		}
		if (currentStyle != null) {
			newSpan.setAttribute("style", currentStyle);
		}

		XmlUtils.treeCopy(currentSpan.getChildNodes(), newSpan);
		xhtmlBlock.appendChild(newSpan);

		newSpan.setAttribute(HEXINID_ATTR, currentSpan.getAttribute(HEXINID_ATTR));

		//System.out.println(XmlUtils.w3CDomNodeToString(xhtmlBlock));

//    	if (nodes.getLength()==1) return;

		for (int i = (startIndex + 1); i < nodes.getLength(); i++) {

			Element thisSpan = ((Element) nodes.item(i));

			// Handle elements other than span eg img
			if (!thisSpan.getLocalName().equals("span")) {
				XmlUtils.treeCopy(thisSpan, xhtmlBlock);

				// Get read for next span
				newSpan = document.createElement("span");
				xhtmlBlock.appendChild(newSpan); // might end up with an empty span
				if (currentClass != null) {
					newSpan.setAttribute("class", currentClass);
				}
				if (currentStyle != null) {
					newSpan.setAttribute("style", currentStyle);
				}
				continue;
			}

			// Handle span
			if (!thisSpan.hasChildNodes()) {
				continue;
			}

			String thisClass = thisSpan.getAttribute("class");
			String thisStyle = thisSpan.getAttribute("style");

			boolean classSame = (currentClass == null && thisClass == null)
					|| (currentClass != null && currentClass.equals(thisClass));
			boolean styleSame = (currentStyle == null && thisStyle == null)
					|| (currentStyle != null && currentStyle.equals(thisStyle));
			// TODO handle case where only difference is "white-space:pre-wrap;"

			if (classSame && styleSame) {
				// add to existing
				XmlUtils.treeCopy(thisSpan.getChildNodes(), newSpan);

			} else {
				newSpan = document.createElement("span");
				if (thisClass != null) {
					newSpan.setAttribute("class", thisClass);
				}
				if (thisStyle != null) {
					newSpan.setAttribute("style", thisStyle);
				}

				XmlUtils.treeCopy(thisSpan.getChildNodes(), newSpan);
				xhtmlBlock.appendChild(newSpan);
				currentClass = thisClass;
				currentStyle = thisStyle;
			}
		}
	}


	public static DocumentFragment createBlockForRPr(
			HTMLConversionContext context,
			String pStyleVal,
			NodeIterator rPrNodeIt,
			NodeIterator childResults, NodeIterator hexinId) {

		if (hexinId!=null) {
			Node node = hexinId.nextNode();
			logger.info("R currentNodeStr:"+node.getTextContent());
			logger.info("R hexinId: "+node.getNodeName());
		} else {
			logger.info("R hexinId: " + hexinId);
		}

		Style defaultRunStyle =
				(context.getWmlPackage().getMainDocumentPart()
				        .getStyleDefinitionsPart(false) != null ?
						context.getWmlPackage().getMainDocumentPart()
						       .getStyleDefinitionsPart(false).getDefaultCharacterStyle() :
						null);

		String defaultCharacterStyleId;
		if (defaultRunStyle.getStyleId() == null) {
			// possible, for non MS source docx
			defaultCharacterStyleId = "DefaultParagraphFont";
		} else {
			defaultCharacterStyleId = defaultRunStyle.getStyleId();
		}

		StyleTree styleTree = context.getWmlPackage().getMainDocumentPart()
		                             .getStyleTree();

		// Note that this is invoked for every paragraph with a pPr node.

		// incoming objects are org.apache.xml.dtm.ref.DTMNodeIterator
		// which implements org.w3c.dom.traversal.NodeIterator

//    	log.info("rPrNode:" + rPrNodeIt.getClass().getName() ); // org.apache.xml.dtm.ref.DTMNodeIterator
//    	log.info("childResults:" + childResults.getClass().getName() );

		try {

			// Get the rPr node as a JAXB object,
			// so we can read it using our standard
			// methods.  Its a bit sad that we
			// can't just adorn our DOM tree with the
			// original JAXB objects?
			RPr rPr = null;
			if (rPrNodeIt != null) { //It is never null
				Node n = rPrNodeIt.nextNode();
				if (n != null) {
					Unmarshaller u = Context.jc.createUnmarshaller();
					u.setEventHandler(new org.docx4j.jaxb.JaxbValidationEventHandler());
					Object jaxb = u.unmarshal(n);
					try {
						rPr = (RPr) jaxb;
					} catch (ClassCastException e) {
						context.getLog().error(
								"Couldn't cast " + jaxb.getClass().getName() + " to RPr!");
					}
				}
			}

			// Our span wraps whatever result tree fragment
			// our style sheet produced when it applied-templates
			// to the child nodes
			Node n = childResults.nextNode();

//	    	System.out.println(XmlUtils.w3CDomNodeToString(n));

			// Create a DOM builder and parse the fragment
			Document document = XmlUtils.getNewDocumentBuilder().newDocument();

			Element span = document.createElement("span");
			document.appendChild(span);

			hexinId(span);

			// Avoid unnecessary nested span for common case of single child
			if (n.hasChildNodes() && n.getChildNodes().getLength() == 1
					&& n.getChildNodes().item(0).getLocalName().equals("span")) {

				String existingStyle = ((Element) n.getChildNodes().item(0))
						.getAttribute("style");
				span.setAttribute("style", existingStyle);
				setSpanAttr(context, defaultCharacterStyleId, styleTree, rPr, span);
				XmlUtils.treeCopy(n.getChildNodes().item(0).getChildNodes(), span);

//        	} else if (!n.getChildNodes().item(0).getLocalName().equals("span")) {
//
//        		// EXP - for backwards compat with old code where w:t didn't create span
//				XmlUtils.treeCopy( n,  span );

			} else {

				setSpanAttr(context, defaultCharacterStyleId, styleTree, rPr, span);
				//XmlUtils.treeCopy( n,  span );
				mergeSpans(n.getChildNodes(), document, span);

			}
			DocumentFragment docfrag = document.createDocumentFragment();
			docfrag.appendChild(document.getDocumentElement());

			return docfrag;


		} catch (Exception e) {
			context.getLog().error(e.getMessage(), e);
		}

		return null;

	}


	/**
	 *
	 */
	private static void setSpanAttr(HTMLConversionContext context,
			String defaultCharacterStyleId, StyleTree styleTree, RPr rPr,
			Element span) {

		// Set @class
		String rStyleVal = defaultCharacterStyleId;
		if (rPr != null && rPr.getRStyle() != null) {
			rStyleVal = rPr.getRStyle().getVal();
		}
		Tree<AugmentedStyle> cTree = styleTree.getCharacterStylesTree();
		org.docx4j.model.styles.Node<AugmentedStyle> asn = cTree.get(rStyleVal);
		if (asn == null) {
			context.getLog()
			       .warn("Can't set @class; No style node for: " + rStyleVal);
		} else {
			String classVal = StyleTree.getHtmlClassAttributeValue(cTree, asn);
			if (classVal != null && !classVal.equals("")) {
				((Element) span).setAttribute("class", classVal);
			}
		}

		if (rPr != null) {

			if (context.getLog().isDebugEnabled()) {
				context.getLog().debug(XmlUtils.marshaltoString(rPr, true, true));
			}

			// Does our rPr contain anything else?
			StringBuilder inlineStyle = new StringBuilder();
			HtmlCssHelper.createCss(context.getWmlPackage(), rPr, inlineStyle);
			if (inlineStyle.toString().equals("")) {
				// Do nothing here - just keep existing style
				// (which if present, is a font definition obtained at w:t level)
			} else {
				String existingStyle = span.getAttribute("style");
				if (existingStyle == null
						|| existingStyle.trim().equals("")) {
					span.setAttribute("style", inlineStyle.toString());
				} else {
					span.setAttribute("style",
							inlineStyle.toString() + ";" + existingStyle);
				}
			}
		}
	}


	private static final AtomicLong HEXINID_ATTR_ACC = new AtomicLong(1);

	private static final String HEXINID_ATTR = "hexinId";

	private static void hexinId(Element xhtmlBlock) {

		xhtmlBlock.setAttribute(HEXINID_ATTR,
				String.valueOf(HEXINID_ATTR_ACC.getAndIncrement()));
	}
}
